package cn.wolfcode.ss.config;

import cn.wolfcode.ss.filter.JwtAuthenticationTokenFilter;
import cn.wolfcode.ss.handler.MyAuthenticationSuccessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

// 使用这个配置后，不会在使用控制台生成的默认用户user和随即密码
// extends WebSecurityConfigurerAdapter继承这个就可以自定义自己的登陆页面

// 总结：使用自定义的登陆和登陆处理
// 1.继承WebSecurityConfigurerAdapter
// 2.重写configure方法

// 支持注解方式进行授权 + 鉴权
// prePostEnabled=true 表示支持：@PreAuthorize 使用
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    // 基于内存-获取用户信息
    // UserDetailsService -- springSecurity 提供专门用于加载用户信息服务层接口
    @Bean
    public UserDetailsService userDetailsService(){
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        // authorities -- 等价于 permission
        // todo: 构建者模式 --- xxx.build()
        manager.createUser(User.withUsername("diaomao").password("666")
                .authorities("dept:insert","dept:update")
                // .roles("dept_mgr")
                // .roles("DEPT_MGR") // 会自动添加ROLE_前缀
                .build());
        manager.createUser(User.withUsername("asan").password("666").authorities("p2").build());
        return manager;
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }

    // 没有使用jwt之前，使用spring security默认的session登录，这个对象框架自动默认操作
    // 现在使用jwt，放弃使用spring security 默认的session登录，意味着需要自己去控制登录逻辑
    // 此时需要在spring security 认证框架规则基础上执行自定义jwt登录认知
    // 所谓认证框架规则基础：还是遵循spring security 认证流程---使用AuthenticationManager 认证管理对象实现认证流程推荐
    @Bean // 这里的bean不要漏掉了，直接交给容器管理
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 禁用csrf安全保护
        http.csrf().disable();

        // url路径权限控制
        http.authorizeRequests()
                .antMatchers("/depts/insert").hasAuthority("dept:insert")
                .antMatchers("/depts/update").hasAuthority("dept:update")
                .antMatchers("/depts/delete").hasAuthority("dept:delete")
                .antMatchers("/depts/list").hasAuthority("dept:list")
                // /jwt/login 自定义登陆接口
                .antMatchers("/jwt/login").permitAll()
                .anyRequest().authenticated();

        // 用户登陆控制
        http.formLogin()
            .successHandler(new MyAuthenticationSuccessHandler())
            .failureHandler((request, response, e)-> {
                response.setContentType("application/json;charset=utf-8");
                String data = "{\"code\":200,\"msg\":\"登陆失败\",\"data\":"+e.getMessage()+"}";
                response.getWriter().write(data);
            });

        // 会话控制--前后端分类项目独立控制用户认证-使用无状态登陆-STATELESS
        http.sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        // 异常控制
        http.exceptionHandling()
                .accessDeniedHandler((request, response, e) -> {
                    response.setContentType("application/json;charset=utf-8");
                    String data = "{\"code\":403,\"msg\":\"没有权限\",\"data\":"+e.getMessage()+"}";
                    response.getWriter().write(data);
                })
                // 当登陆失败后的处理逻辑
                .authenticationEntryPoint((request, response, e) -> {
                    response.setContentType("application/json;charset=utf-8");
                    response.getWriter().write("has error: " + e.getMessage());
                });

        // 过滤器控制
        // 将自定义的jwt拦截过滤器放置默认认证过滤器前面，保证认证流程先执行自定义jwt过滤
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }
}
